﻿using System;
using FLMScan.Geometry;
using System.Collections.Generic;
using AForge.Imaging;
using System.Drawing;
using log4net;
using FLMScan.OpenCV;
using FLMScan.Capture;

namespace FLMScan
{
    class ImageProcessing
    {
        private ILog logger;

        private const double MinSquareRatio = 0.80;
        private const double MaxSquareRatio = 1.2;
        private const int MinLaserLineWidth = 1;
        private const int MinLaserLineHeight = 270;
        private const int MaxLaserLineWidth = 50;
        private const int MaxLaserLineHeight = 1000;
        private const int MinLedColorValue = 220;
        private const int MinLaserColorValue = 240;

        public ImageProcessing()
        {
            logger = LogManager.GetLogger(typeof(ImageProcessing));
        }

        public List<FlmPoint> GetLaserPoints(CapturedFrame frame)
        {
            return CaptureWrapperOpenCV.PointsAboveThreshold(frame.Frame, 2, 100);
            // return CaptureWrapperOpenCV.GoodFeaturesToTrack(frame.Frame);
        }

        // TBD: improve code by using more advanced sorting algorithm
        public List<FlmPoint> GetCalibrationPoints(CapturedFrame frame)
        {
            List<FlmPoint> calibrationPoints = new List<FlmPoint>();

            BlobCounter blobCounter = new BlobCounter();
            blobCounter.BackgroundThreshold = Color.FromArgb(MinLedColorValue, MinLedColorValue, MinLedColorValue);
            blobCounter.ProcessImage(frame.Bitmap);                        
            Rectangle[] rects = blobCounter.GetObjectsRectangles();
            List<int> biggestRectangleIndices = new List<int>();
            
            
            int maxWidth = 0;
            foreach (Rectangle rect in rects)
            {
                if (rect.Width > maxWidth)
                    maxWidth = rect.Width;
            }

            // iterate through all rectangle sizes, beggining at maximum size
            for (int size = maxWidth; size > 0; size--)
            {
                int index = 0;

                // iterate through all rectangles
                foreach (Rectangle rect in rects)
                {
                    // check only squares
                    if (IsSquare(rect))
                    {                                            
                        // current rectangle is at least as big as current size    
                        if (rect.Width >= size)
                        {
                            // current rectangle was not saved yet 
                            if (!biggestRectangleIndices.Contains(index))
                            {
                                biggestRectangleIndices.Add(index);
                                if (biggestRectangleIndices.Count == 4)
                                    break;
                            }
                        }                        
                    }

                    index++;
                }

                if (biggestRectangleIndices.Count == 4)
                    break;
            }

            
            int count = 0;            
            foreach (int rectangleIndex in biggestRectangleIndices)
            {
                Rectangle rect = rects[rectangleIndex];                

                FlmPoint calibrationPoint = new FlmPoint(rect.Location.X + (rect.Width / 2), rect.Location.Y + (rect.Height / 2));
                calibrationPoints.Add(calibrationPoint);
            
                calibrationPoint.Log("Calibration board LED detected. CalibrationPoint:");

                ++count;

                if (calibrationPoints.Count == 4)
                    break;
            }

            
                
            return calibrationPoints;
        }

        public List<FlmPoint> GetLaserLine(CapturedFrame frame)
        {
            List<FlmPoint> calibrationPoints = new List<FlmPoint>();

            BlobCounter blobCounter = new BlobCounter();
            blobCounter.BackgroundThreshold = Color.FromArgb(MinLaserColorValue, MinLaserColorValue, MinLaserColorValue);
            blobCounter.ProcessImage(frame.Bitmap);            
            Rectangle[] rects = blobCounter.GetObjectsRectangles();

            bool foundLaserLine = false;
            int maxHeight = 0;
            FlmPoint longestLaserLineStart = new FlmPoint();
            FlmPoint longestLaserLineEnd = new FlmPoint(); 

            // analyze and classify rectangles
            foreach (Rectangle rect in rects)
            {
                if (HasExpectedSize(rect, MinLaserLineWidth, MinLaserLineHeight, MaxLaserLineWidth, MaxLaserLineHeight))
                {
                    foundLaserLine = true;

                    // TBD: this calculation is very simplistic - might be problematically for diagonal laser lines
                    // rather: calculate narrow laser lines (vertical) like below, and calculate diagonal laser lines
                    // differently
                    FlmPoint laserLineStart = new FlmPoint(rect.Location.X + (rect.Width / 2), rect.Location.Y);
                    FlmPoint laserLineEnd = new FlmPoint(rect.Location.X + (rect.Width / 2), rect.Location.Y + rect.Height);

                    if (rect.Height > maxHeight)
                    {
                        maxHeight = rect.Height;
                        longestLaserLineStart = laserLineStart;
                        longestLaserLineEnd = laserLineEnd;
                    }
                }
            }

            if (foundLaserLine)
            {
                calibrationPoints.Add(longestLaserLineStart);
                calibrationPoints.Add(longestLaserLineEnd);

                if (logger.IsDebugEnabled)
                {
                    longestLaserLineStart.Log("Laser line start detected. laserLineStart:");
                    longestLaserLineEnd.Log("Laser line end detected. laserLineEnd:");
                }
            }

            return calibrationPoints;
        }

        private bool IsSquare(Rectangle rect)
        {
            double ratio = (double)rect.Width / (double)rect.Height;
            return (ratio >= MinSquareRatio && ratio <= MaxSquareRatio);         
        }

        private bool HasExpectedSize(Rectangle rect, int minSizeX, int minSizeY, int maxSizeX, int maxSizeY)
        {
            bool hasMinSize = ((rect.Width >= minSizeX) && (rect.Height >= minSizeY));
            bool isNotBiggerThanMaxSize = ((rect.Width <= maxSizeX) && (rect.Height <= maxSizeY));

            return (hasMinSize && isNotBiggerThanMaxSize);
        }
    }
}
